using Net35.Xrm.Sdk.Linq;
using Net35.Xrm.Sdk.Messages;
using Net35.Xrm.Sdk.Metadata;
using Net35.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
namespace Net35.Xrm.Sdk.Client
{
	public class OrganizationServiceContext : IDisposable
	{
		private static class Strings
		{
			public const string PropertyDoesNotExist = "The property '{0}' does not exist on the entity '{1}'.";
			public const string NoSettableProperty = "The closed type '{0}' does not have a corresponding '{1}' settable property.";
			public const string RequiresCreatedOrChangedState = "The '{0}' entity must be in the created or changed state.";
			public const string RequiresUnchangedState = "The '{0}' entity must be in the default (null) or unchanged state.";
			public const string RequiresCreatedState = "The '{0}' entity must be in the default (null) or created state.";
			public const string AlreadyTrackingEntity = "The context is already tracking the '{0}' entity.";
			public const string AlreadyTrackingRelationship = "The context is already tracking the '{0}' relationship.";
			public const string EmptyId = "The '{0}' entity has an empty ID.";
			public const string AlreadyTrackingIdentity = "The context is already tracking a different '{0}' entity with the same identity.";
			public const string EntityAlreadyAttached = "The '{0}' entity is already attached to a context.";
			public const string NotSubclass = "The specified type '{0}' must be a subclass of '{1}'.";
			public const string NotKnownEntityType = "The specified type '{0}' is not a known entity type.";
			public const string NotTrackingEntity = "The context is not currently tracking the '{0}' entity.";
			public const string RelationshipEndIsDeleted = "One or both of the ends of the '{0}' relationship is in the deleted state.";
			public const string RelationshipEndIsAdded = "One or both of the ends of the '{0}' relationship is in the added state.";
			public const string CannotLoadAddedEntity = "The context can not load the related collection or reference for entities in the added state.";
			public const string CannotLoadAttachedEntity = "The context can not load the related collection or reference for tracked entities while the 'MergeOption' is set to 'NoTracking'. Change the 'MergeOption' value or detach the '{0}' entity.";
			public const string CannotLoadDetachedEntity = "The context can not load the related collection or reference for untracked entities while the 'MergeOption' is not set to 'NoTracking'. Change the 'MergeOption' to 'NoTracking' or attach the '{0}' entity.";
			public const string SourceEqualsTarget = "The entity cannot link to itself.";
		}
		private readonly IOrganizationService _service;
		private readonly Dictionary<Entity, EntityDescriptor> _descriptors;
		private readonly Dictionary<EntityReference, EntityDescriptor> _identityToDescriptor;
		private readonly Dictionary<LinkDescriptor, LinkDescriptor> _bindings;
		private readonly HashSet<Entity> _roots;
		private readonly bool _trackEntityChanges;
		protected virtual IQueryProvider QueryProvider
		{
			get;
			private set;
		}
		public MergeOption MergeOption
		{
			get;
			set;
		}
		public SaveChangesOptions SaveChangesDefaultOptions
		{
			get;
			set;
		}
		public OrganizationServiceContext(IOrganizationService service) : this(service, true)
		{
		}
		internal OrganizationServiceContext(IOrganizationService service, bool trackEntityChanges)
		{
			if (service == null)
			{
				throw new ArgumentNullException("service");
			}
			this._service = service;
			this._descriptors = new Dictionary<Entity, EntityDescriptor>(EqualityComparer<Entity>.Default);
			this._identityToDescriptor = new Dictionary<EntityReference, EntityDescriptor>(EqualityComparer<EntityReference>.Default);
			this._bindings = new Dictionary<LinkDescriptor, LinkDescriptor>(LinkDescriptor.EquivalenceComparer);
			this._roots = new HashSet<Entity>(EqualityComparer<Entity>.Default);
			this._trackEntityChanges = trackEntityChanges;
			this.QueryProvider = new QueryProvider(this);
			this.MergeOption = MergeOption.AppendOnly;
		}
		~OrganizationServiceContext()
		{
			this.Dispose(false);
		}
		public IQueryable<TEntity> CreateQuery<TEntity>() where TEntity : Entity
		{
			OrganizationServiceContext.CheckEntitySubclass(typeof(TEntity));
			string nameForType = KnownProxyTypesProvider.GetInstance(true).GetNameForType(typeof(TEntity));
			return this.CreateQuery<TEntity>(this.QueryProvider, nameForType);
		}
		public IQueryable<Entity> CreateQuery(string entityLogicalName)
		{
			if (StringExtensions.IsNullOrWhiteSpace(entityLogicalName))
			{
				throw new ArgumentNullException("entityLogicalName");
			}
			return this.CreateQuery<Entity>(this.QueryProvider, entityLogicalName);
		}
		protected virtual IQueryable<TEntity> CreateQuery<TEntity>(IQueryProvider provider, string entityLogicalName)
		{
			return new Query<TEntity>(provider, entityLogicalName);
		}
		public void LoadProperty(Entity entity, string propertyName)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			if (StringExtensions.IsNullOrWhiteSpace(propertyName))
			{
				throw new ArgumentNullException("propertyName");
			}
			Type type = entity.GetType();
			OrganizationServiceContext.CheckEntitySubclass(type);
			PropertyInfo property = type.GetProperty(propertyName);
			if (property == null)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The property '{0}' does not exist on the entity '{1}'.", new object[]
				{
					propertyName,
					entity
				}));
			}
			RelationshipSchemaNameAttribute firstOrDefaultCustomAttribute = property.GetFirstOrDefaultCustomAttribute<RelationshipSchemaNameAttribute>();
			if (firstOrDefaultCustomAttribute != null)
			{
				Relationship relationship = firstOrDefaultCustomAttribute.Relationship;
				this.LoadProperty(entity, relationship);
				return;
			}
			AttributeLogicalNameAttribute firstOrDefaultCustomAttribute2 = property.GetFirstOrDefaultCustomAttribute<AttributeLogicalNameAttribute>();
			if (firstOrDefaultCustomAttribute2 != null)
			{
				this.LoadProperty(entity, firstOrDefaultCustomAttribute2);
				return;
			}
			throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The closed type '{0}' does not have a corresponding '{1}' settable property.", new object[]
			{
				type,
				propertyName
			}));
		}
		public void LoadProperty(Entity entity, Relationship relationship)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			EntityDescriptor entityDescriptor;
			bool flag = this._descriptors.TryGetValue(entity, out entityDescriptor);
			if (flag)
			{
				if (this.MergeOption == MergeOption.NoTracking)
				{
					throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context can not load the related collection or reference for tracked entities while the 'MergeOption' is set to 'NoTracking'. Change the 'MergeOption' value or detach the '{0}' entity.", new object[]
					{
						entity.LogicalName
					}));
				}
				if (entityDescriptor.State == EntityStates.Added)
				{
					throw new InvalidOperationException("The context can not load the related collection or reference for entities in the added state.");
				}
			}
			else if (this.MergeOption != MergeOption.NoTracking)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context can not load the related collection or reference for untracked entities while the 'MergeOption' is not set to 'NoTracking'. Change the 'MergeOption' to 'NoTracking' or attach the '{0}' entity.", new object[]
				{
					entity.LogicalName
				}));
			}
			EntityCollection relatedEntities = this.GetRelatedEntities(entity, relationship);
			if (relatedEntities != null)
			{
				foreach (Entity current in relatedEntities.Entities)
				{
					Entity target = this.MergeEntity(current);
					this.MergeRelationship(entity, relationship, target, flag);
				}
			}
		}
		private void LoadProperty(Entity entity, AttributeLogicalNameAttribute attribute)
		{
			if (this.MergeOption != MergeOption.NoTracking)
			{
				EntityDescriptor entityDescriptor = this.EnsureTracked(entity, "entity");
				if (entityDescriptor.State == EntityStates.Added)
				{
					throw new InvalidOperationException("The context can not load the related collection or reference for entities in the added state.");
				}
			}
			string logicalName = attribute.LogicalName;
			RetrieveResponse retrieveResponse = (RetrieveResponse)this.Execute(new RetrieveRequest
			{
				Target = new EntityReference(entity.LogicalName, entity.Id),
				ColumnSet = new ColumnSet(new string[]
				{
					logicalName
				})
			});
			if (retrieveResponse != null && retrieveResponse.Entity != null && retrieveResponse.Entity.Attributes.Contains(logicalName))
			{
				entity.Attributes[logicalName] = retrieveResponse.Entity.Attributes[logicalName];
			}
		}
		public void Attach(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			this.ValidateAttach(entity, EntityStates.Unchanged);
			this.AttachEntityGraph(entity, EntityStates.Unchanged);
		}
		public bool Detach(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			return this.Detach(entity, true);
		}
		public bool Detach(Entity entity, bool recursive)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			EntityDescriptor existingEntity;
			if (!this._descriptors.TryGetValue(entity, out existingEntity))
			{
				return false;
			}
			if (!recursive)
			{
				this.DetachEntityTracking(existingEntity);
				foreach (Tuple<Entity, Relationship, Entity> current in OrganizationServiceContext.TraverseRelatedEntities(entity))
				{
					Entity item = current.Item1;
					Relationship item2 = current.Item2;
					Entity item3 = current.Item3;
					LinkDescriptor existingLink;
					if (this._bindings.TryGetValue(new LinkDescriptor(item, item2, item3), out existingLink))
					{
						this.DetachLinkTracking(existingLink);
					}
				}
				using (List<LinkDescriptor>.Enumerator enumerator2 = this.GetTargetingLinks(entity).ToList<LinkDescriptor>().GetEnumerator())
				{
					while (enumerator2.MoveNext())
					{
						LinkDescriptor current2 = enumerator2.Current;
						this.DetachLinkTrackingAndRemoveEntity(current2);
					}
					return true;
				}
			}
			IEnumerable<Entity> enumerable = this.DetachEntityGraph(entity);
			foreach (Entity current3 in enumerable)
			{
				foreach (LinkDescriptor current4 in this.GetTargetingLinks(current3).ToList<LinkDescriptor>())
				{
					this.DetachLinkTrackingAndRemoveEntity(current4);
				}
			}
			return true;
		}
		public void AttachLink(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			this.EnsureRelatable(source, relationship, target, EntityStates.Unchanged);
			LinkDescriptor linkDescriptor = new LinkDescriptor(source, relationship, target);
			if (this.IsAttached(linkDescriptor))
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context is already tracking the '{0}' relationship.", new object[]
				{
					relationship.SchemaName
				}));
			}
			this.AttachLinkTracking(linkDescriptor);
			OrganizationServiceContext.AddRelationshipIfNotContains(source, relationship, target);
		}
		public bool DetachLink(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			LinkDescriptor existingLink;
			if (!this._bindings.TryGetValue(new LinkDescriptor(source, relationship, target), out existingLink))
			{
				return false;
			}
			this.DetachLinkTrackingAndRemoveEntity(existingLink);
			return true;
		}
		public IEnumerable<Entity> GetAttachedEntities()
		{
			return 
				from d in this._descriptors.Values
				select d.Entity;
		}
		public bool IsAttached(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			return this._descriptors.ContainsKey(entity);
		}
		public bool IsDeleted(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			EntityDescriptor entityDescriptor;
			return this._descriptors.TryGetValue(entity, out entityDescriptor) && entityDescriptor.State == EntityStates.Deleted;
		}
		public bool IsAttached(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			return this.IsAttached(new LinkDescriptor(source, relationship, target));
		}
		private bool IsAttached(LinkDescriptor link)
		{
			return this._bindings.ContainsKey(link);
		}
		public bool IsDeleted(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			LinkDescriptor linkDescriptor;
			return this._bindings.TryGetValue(new LinkDescriptor(source, relationship, target), out linkDescriptor) && linkDescriptor.State == EntityStates.Deleted;
		}
		private void ValidateAttach(Entity graph, EntityStates state)
		{
			OrganizationServiceContext.TraverseEntityGraph(graph, delegate(Entity entity)
			{
				if (entity == graph)
				{
					if (this.IsAttached(entity))
					{
						throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context is already tracking the '{0}' entity.", new object[]
						{
							entity.LogicalName
						}));
					}
				}
				else if (this.IsAttached(entity))
				{
					return;
				}
				if (state == EntityStates.Unchanged && entity.EntityState.HasValue && !(entity.EntityState == EntityState.Unchanged))
				{
					throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The '{0}' entity must be in the default (null) or unchanged state.", new object[]
					{
						entity.LogicalName
					}));
				}
				if (state == EntityStates.Added && entity.EntityState.HasValue && !(entity.EntityState == EntityState.Created))
				{
					throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The '{0}' entity must be in the default (null) or created state.", new object[]
					{
						entity.LogicalName
					}));
				}
				if (state != EntityStates.Added && entity.Id == Guid.Empty)
				{
					throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The '{0}' entity has an empty ID.", new object[]
					{
						entity.LogicalName
					}));
				}
				EntityDescriptor entityDescriptor;
				if (entity.Id != Guid.Empty && this._identityToDescriptor.TryGetValue(entity.ToEntityReference(), out entityDescriptor))
				{
					throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context is already tracking a different '{0}' entity with the same identity.", new object[]
					{
						entity.LogicalName
					}));
				}
				if (entity.IsReadOnly)
				{
					throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The '{0}' entity is already attached to a context.", new object[]
					{
						entity.LogicalName
					}));
				}
			}, delegate(Entity source, Relationship relationship, Entity target)
			{
			}).ToList<Entity>();
		}
		private void AttachEntityGraph(Entity graph, EntityStates state)
		{
			OrganizationServiceContext.TraverseEntityGraph(graph, delegate(Entity entity)
			{
				this.AttachEntityTracking(new EntityDescriptor(state, entity.ToEntityReference(), entity));
			}, delegate(Entity source, Relationship relationship, Entity target)
			{
				this.AttachLinkTracking(state, source, relationship, target);
			}).ToList<Entity>();
		}
		private void AttachEntityTracking(EntityDescriptor newEntity)
		{
			if (this.IsAttached(newEntity.Entity))
			{
				return;
			}
			if (newEntity.Identity.Id != Guid.Empty)
			{
				this._identityToDescriptor.Add(newEntity.Identity, newEntity);
			}
			this._descriptors.Add(newEntity.Entity, newEntity);
			if (this._trackEntityChanges)
			{
				newEntity.Entity.IsReadOnly = true;
			}
			Entity arg_92_0 = newEntity.Entity;
			EntityState? entityState = OrganizationServiceContext.MapEntityState(newEntity.State);
			arg_92_0.SetEntityStateInternal(entityState.HasValue ? new EntityState?(entityState.GetValueOrDefault()) : newEntity.Entity.EntityState);
			this.OnBeginEntityTracking(newEntity.Entity);
		}
		private static EntityState? MapEntityState(EntityStates state)
		{
			if (state == EntityStates.Unchanged)
			{
				return new EntityState?(EntityState.Unchanged);
			}
			if (state == EntityStates.Added)
			{
				return new EntityState?(EntityState.Created);
			}
			if (state == EntityStates.Modified)
			{
				return new EntityState?(EntityState.Changed);
			}
			return null;
		}
		private void AttachLinkTracking(EntityStates state, Entity source, Relationship relationship, Entity target)
		{
			EntityStates entityStates = (source.EntityState == EntityState.Created || target.EntityState == EntityState.Created) ? EntityStates.Added : state;
			if (entityStates == EntityStates.Added)
			{
				this._roots.Add(source);
			}
			this.AttachLinkTracking(new LinkDescriptor(entityStates, source, relationship, target));
		}
		private void AttachLinkTracking(LinkDescriptor newLink)
		{
			if (this.IsAttached(newLink.Source, newLink.Relationship, newLink.Target))
			{
				return;
			}
			this._bindings.Add(newLink, newLink);
			this.OnBeginLinkTracking(newLink.Source, newLink.Relationship, newLink.Target);
		}
		private IEnumerable<Entity> DetachEntityGraph(Entity graph)
		{
			return this.TraverseEntityGraph(graph, new Action<EntityDescriptor>(this.DetachEntityTracking), new Action<LinkDescriptor>(this.DetachLinkTracking)).ToList<Entity>();
		}
		private void DetachEntityTracking(EntityDescriptor existingEntity)
		{
			if (this._trackEntityChanges)
			{
				existingEntity.Entity.IsReadOnly = false;
			}
			existingEntity.State = EntityStates.Detached;
			bool flag = this._descriptors.Remove(existingEntity.Entity);
			this._identityToDescriptor.Remove(existingEntity.Entity.ToEntityReference());
			this._roots.Remove(existingEntity.Entity);
			if (flag)
			{
				this.OnEndEntityTracking(existingEntity.Entity);
			}
		}
		private void DetachLinkTracking(LinkDescriptor existingLink)
		{
			existingLink.State = EntityStates.Detached;
			bool flag = this._bindings.Remove(existingLink);
			if (flag)
			{
				this.OnEndLinkTracking(existingLink.Source, existingLink.Relationship, existingLink.Target);
			}
		}
		private void DetachLinkTrackingAndRemoveEntity(LinkDescriptor existingLink)
		{
			this.DetachLinkTracking(existingLink);
			OrganizationServiceContext.RemoveRelationshipIfContains(existingLink);
		}
		public void AddLink(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			this.EnsureRelatable(source, relationship, target, EntityStates.Added);
			LinkDescriptor linkDescriptor = new LinkDescriptor(EntityStates.Added, source, relationship, target);
			if (this.IsAttached(linkDescriptor))
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context is already tracking the '{0}' relationship.", new object[]
				{
					relationship.SchemaName
				}));
			}
			this.AttachLinkTracking(linkDescriptor);
			OrganizationServiceContext.AddRelationshipIfNotContains(source, relationship, target);
			this._roots.Add(source);
		}
		public void DeleteLink(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			bool flag = this.EnsureRelatable(source, relationship, target, EntityStates.Deleted);
			LinkDescriptor linkDescriptor = new LinkDescriptor(source, relationship, target);
			LinkDescriptor linkDescriptor2;
			if (this._bindings.TryGetValue(linkDescriptor, out linkDescriptor2) && linkDescriptor2.State == EntityStates.Added)
			{
				this.DeleteLinkTrackingAndRemoveEntity(linkDescriptor2);
				return;
			}
			if (flag)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "One or both of the ends of the '{0}' relationship is in the added state.", new object[]
				{
					relationship.SchemaName
				}));
			}
			if (linkDescriptor2 == null)
			{
				this.AttachLinkTracking(linkDescriptor);
				linkDescriptor2 = linkDescriptor;
			}
			if (linkDescriptor2.State != EntityStates.Deleted)
			{
				linkDescriptor2.State = EntityStates.Deleted;
				OrganizationServiceContext.RemoveRelationshipIfContains(linkDescriptor2);
			}
		}
		public void AddObject(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			this.ValidateAttach(entity, EntityStates.Added);
			this.AttachEntityGraph(entity, EntityStates.Added);
			this._roots.Add(entity);
		}
		public void AddRelatedObject(Entity source, Relationship relationship, Entity target)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			EntityDescriptor entityDescriptor = this.EnsureTracked(source, "source");
			if (entityDescriptor.State == EntityStates.Deleted)
			{
				throw new InvalidOperationException("AddRelatedObject method only works if the source entity is in a non-deleted state.");
			}
			this.AddObject(target);
			this.AddLink(source, relationship, target);
		}
		public void DeleteObject(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			this.DeleteObject(entity, false);
		}
		public void DeleteObject(Entity entity, bool recursive)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			EntityDescriptor existingEntity = this.EnsureTracked(entity, "entity");
			if (!recursive)
			{
				this.DeleteEntityTracking(existingEntity);
				foreach (Tuple<Entity, Relationship, Entity> current in OrganizationServiceContext.TraverseRelatedEntities(entity))
				{
					Entity item = current.Item1;
					Relationship item2 = current.Item2;
					Entity item3 = current.Item3;
					LinkDescriptor linkDescriptor;
					if (this._bindings.TryGetValue(new LinkDescriptor(item, item2, item3), out linkDescriptor))
					{
						linkDescriptor.State = EntityStates.Deleted;
					}
				}
				using (List<LinkDescriptor>.Enumerator enumerator2 = this.GetTargetingLinks(entity).ToList<LinkDescriptor>().GetEnumerator())
				{
					while (enumerator2.MoveNext())
					{
						LinkDescriptor current2 = enumerator2.Current;
						this.DeleteLinkTrackingAndRemoveEntity(current2);
					}
					return;
				}
			}
			IEnumerable<Entity> enumerable = this.DeleteEntityGraph(entity);
			foreach (Entity current3 in enumerable)
			{
				foreach (LinkDescriptor current4 in this.GetTargetingLinks(current3).ToList<LinkDescriptor>())
				{
					this.DeleteLinkTrackingAndRemoveEntity(current4);
				}
			}
		}
		private IEnumerable<Entity> DeleteEntityGraph(Entity entity)
		{
			return this.TraverseEntityGraph(entity, new Action<EntityDescriptor>(this.DeleteEntityTracking), new Action<LinkDescriptor>(this.DeleteLinkTracking)).ToList<Entity>();
		}
		private void DeleteEntityTracking(EntityDescriptor existingEntity)
		{
			EntityStates state = existingEntity.State;
			if (state == EntityStates.Added)
			{
				this.DetachEntityTracking(existingEntity);
			}
			else if (state != EntityStates.Deleted)
			{
				existingEntity.State = EntityStates.Deleted;
			}
			this._roots.Remove(existingEntity.Entity);
		}
		private void DeleteLinkTracking(LinkDescriptor existingLink)
		{
			EntityStates state = existingLink.State;
			if (state == EntityStates.Added)
			{
				this.DetachLinkTracking(existingLink);
				return;
			}
			if (state != EntityStates.Deleted)
			{
				existingLink.State = EntityStates.Deleted;
			}
		}
		private void DeleteLinkTrackingAndRemoveEntity(LinkDescriptor existingLink)
		{
			this.DeleteLinkTracking(existingLink);
			OrganizationServiceContext.RemoveRelationshipIfContains(existingLink);
		}
		public void UpdateObject(Entity entity)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			this.UpdateObject(entity, false);
		}
		public void UpdateObject(Entity entity, bool recursive)
		{
			if (entity == null)
			{
				throw new ArgumentNullException("entity");
			}
			EntityDescriptor existingEntity = this.EnsureTracked(entity, "entity");
			if (!recursive)
			{
				this.InternalUpdate(existingEntity);
				return;
			}
			this.ValidateUpdate(entity);
			this.UpdateEntityGraph(entity);
		}
		private void ValidateUpdate(Entity graph)
		{
			OrganizationServiceContext.TraverseEntityGraph(graph, delegate(Entity entity)
			{
				if (!this.IsAttached(entity))
				{
					throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context is not currently tracking the '{0}' entity.", new object[]
					{
						entity.LogicalName
					}));
				}
			}, delegate(Entity source, Relationship relationship, Entity target)
			{
			}).ToList<Entity>();
		}
		private void UpdateEntityGraph(Entity entity)
		{
			this.TraverseEntityGraph(entity, new Action<EntityDescriptor>(this.InternalUpdate), delegate(LinkDescriptor link)
			{
			}).ToList<Entity>();
		}
		private void InternalUpdate(EntityDescriptor existingEntity)
		{
			if (existingEntity.State == EntityStates.Unchanged)
			{
				existingEntity.State = EntityStates.Modified;
				existingEntity.Entity.SetEntityStateInternal(new EntityState?(EntityState.Changed));
				this._roots.Add(existingEntity.Entity);
			}
		}
		public OrganizationResponse Execute(OrganizationRequest request)
		{
			if (request == null)
			{
				throw new ArgumentNullException("request");
			}
			this.OnExecuting(request);
			OrganizationResponse organizationResponse;
			try
			{
				organizationResponse = this._service.Execute(request);
			}
			catch (Exception exception)
			{
				this.OnExecute(request, exception);
				throw;
			}
			this.OnExecute(request, organizationResponse);
			return organizationResponse;
		}
		public void ClearChanges()
		{
			List<LinkDescriptor> list = this._bindings.Values.ToList<LinkDescriptor>();
			foreach (LinkDescriptor current in list)
			{
				this.DetachLinkTracking(current);
			}
			List<EntityDescriptor> list2 = this._descriptors.Values.ToList<EntityDescriptor>();
			foreach (EntityDescriptor current2 in list2)
			{
				this.DetachEntityTracking(current2);
			}
			this._bindings.Clear();
			this._descriptors.Clear();
			this._identityToDescriptor.Clear();
			this._roots.Clear();
		}
		public SaveChangesResultCollection SaveChanges()
		{
			return this.SaveChanges(this.SaveChangesDefaultOptions);
		}
		public SaveChangesResultCollection SaveChanges(SaveChangesOptions options)
		{
			this.OnSavingChanges(options);
			SaveChangesResultCollection saveChangesResultCollection = new SaveChangesResultCollection(options);
			foreach (Tuple<DisassociateRequest, IEnumerable<LinkDescriptor>> current in this.GetDisassociateRequests().ToList<Tuple<DisassociateRequest, IEnumerable<LinkDescriptor>>>())
			{
				SaveChangesResult saveChangesResult = this.SaveChange(current.Item1, saveChangesResultCollection);
				if (!OrganizationServiceContext.CanContinue(options, saveChangesResult))
				{
					this.OnSaveChanges(saveChangesResultCollection);
					throw new SaveChangesException(saveChangesResult.Error, saveChangesResultCollection);
				}
				foreach (LinkDescriptor current2 in current.Item2)
				{
					this.DetachLinkTracking(current2);
				}
			}
			foreach (EntityDescriptor current3 in this.GetDeletedEntities().ToList<EntityDescriptor>())
			{
				EntityReference target = current3.Entity.ToEntityReference();
				DeleteRequest request = new DeleteRequest
				{
					Target = target
				};
				SaveChangesResult saveChangesResult2 = this.SaveChange(request, saveChangesResultCollection);
				if (!OrganizationServiceContext.CanContinue(options, saveChangesResult2))
				{
					this.OnSaveChanges(saveChangesResultCollection);
					throw new SaveChangesException(saveChangesResult2.Error, saveChangesResultCollection);
				}
				this.DetachEntityTracking(current3);
				foreach (LinkDescriptor current4 in this.GetTargetingLinks(current3.Entity).ToList<LinkDescriptor>())
				{
					this.DetachLinkTracking(current4);
				}
			}
			List<Entity> list = OrganizationServiceContext.FilterRoots(new HashSet<Entity>(this._roots)).ToList<Entity>();
			foreach (Entity current5 in list)
			{
				foreach (SaveChangesResult current6 in this.GetChangeRequests(saveChangesResultCollection, current5))
				{
					if (!OrganizationServiceContext.CanContinue(options, current6))
					{
						this.OnSaveChanges(saveChangesResultCollection);
						throw new SaveChangesException(current6.Error, saveChangesResultCollection);
					}
				}
			}
			this.DetachAllOnSave();
			this.OnSaveChanges(saveChangesResultCollection);
			return saveChangesResultCollection;
		}
		private static IEnumerable<Entity> FilterRoots(ICollection<Entity> roots)
		{
			HashSet<Entity> hashSet = new HashSet<Entity>();
			while (roots.Any<Entity>())
			{
				OrganizationServiceContext.FilterRoots(hashSet, roots);
			}
			return hashSet;
		}
		private static void FilterRoots(ICollection<Entity> filtered, ICollection<Entity> unfiltered)
		{
			Entity root = unfiltered.First<Entity>();
			OrganizationServiceContext.TraverseEntityGraph(root, delegate(Entity entity)
			{
				if (entity != root)
				{
					filtered.Remove(entity);
					unfiltered.Remove(entity);
				}
			}, delegate(Entity s, Relationship r, Entity t)
			{
			}).ToList<Entity>();
			unfiltered.Remove(root);
			filtered.Add(root);
		}
		private bool CanDeleteRelationship(LinkDescriptor link)
		{
			EntityDescriptor entityDescriptor;
			EntityDescriptor entityDescriptor2;
			return link.State == EntityStates.Deleted && this._descriptors.TryGetValue(link.Source, out entityDescriptor) && entityDescriptor.State != EntityStates.Deleted && this._descriptors.TryGetValue(link.Target, out entityDescriptor2) && entityDescriptor2.State != EntityStates.Deleted;
		}
		private static bool CanContinue(SaveChangesOptions options, SaveChangesResult result)
		{
			return (options & SaveChangesOptions.ContinueOnError) == SaveChangesOptions.ContinueOnError || result.Error == null;
		}
		private SaveChangesResult SaveChange(OrganizationRequest request, IList<SaveChangesResult> results)
		{
			SaveChangesResult saveChangesResult;
			try
			{
				OrganizationResponse response = this.Execute(request);
				saveChangesResult = new SaveChangesResult(request, response);
			}
			catch (Exception error)
			{
				saveChangesResult = new SaveChangesResult(request, error);
			}
			results.Add(saveChangesResult);
			return saveChangesResult;
		}
		private IEnumerable<Tuple<DisassociateRequest, IEnumerable<LinkDescriptor>>> GetDisassociateRequests()
		{
			var enumerable = 
				from b in this._bindings.Values.Where(new Func<LinkDescriptor, bool>(this.CanDeleteRelationship))
				group b by new
				{
					b.Source,
					b.Relationship
				};
			foreach (var current in enumerable)
			{
				EntityReference target = current.Key.Source.ToEntityReference();
				Relationship relationship = current.Key.Relationship;
				List<LinkDescriptor> list = current.ToList<LinkDescriptor>();
				EntityReferenceCollection entityReferenceCollection = new EntityReferenceCollection();
				entityReferenceCollection.AddRange(
					from grouping in list
					select grouping.Target.ToEntityReference());
				yield return new Tuple<DisassociateRequest, IEnumerable<LinkDescriptor>>(new DisassociateRequest
				{
					Target = target,
					Relationship = relationship,
					RelatedEntities = entityReferenceCollection
				}, list);
			}
			yield break;
		}
		private IEnumerable<EntityDescriptor> GetDeletedEntities()
		{
			return 
				from d in this._descriptors.Values
				where d.State == EntityStates.Deleted
				select d;
		}
		private IEnumerable<SaveChangesResult> GetChangeRequests(SaveChangesResultCollection results, Entity entity)
		{
			List<Entity> path = new List<Entity>
			{
				entity
			};
			List<LinkDescriptor> list = new List<LinkDescriptor>();
			IEnumerable<SaveChangesResult> enumerable = (entity.EntityState == EntityState.Unchanged) ? this.GetChangeRequestsFromUnchangedTree(results, entity, path, list) : this.GetChangeRequestsFromChangedTree(results, entity, path, list);
			foreach (SaveChangesResult current in enumerable)
			{
				yield return current;
			}
			if (list.Any<LinkDescriptor>())
			{
				IEnumerable<SaveChangesResult> enumerable2 = this.ToAssociateResults(list, results);
				foreach (SaveChangesResult current2 in enumerable2)
				{
					yield return current2;
				}
			}
			yield break;
		}
		private IEnumerable<SaveChangesResult> GetChangeRequestsFromChangedTree(SaveChangesResultCollection results, Entity entity, IEnumerable<Entity> path, IList<LinkDescriptor> circularLinks)
		{
			List<Entity> localPath = new List<Entity>
			{
				entity
			};
			List<LinkDescriptor> list = new List<LinkDescriptor>();
			IEnumerable<SaveChangesResult> changeRequestsFromSubtree = this.GetChangeRequestsFromSubtree(results, entity, localPath, list, path, circularLinks);
			foreach (SaveChangesResult current in changeRequestsFromSubtree)
			{
				yield return current;
			}
			SaveChangesResult saveChangesResult = this.GetSaveChangesResult(results, entity);
			yield return saveChangesResult;
			this.DetachOnSave(entity);
			if (list.Any<LinkDescriptor>())
			{
				IEnumerable<SaveChangesResult> enumerable = this.ToAssociateResults(list, results);
				foreach (SaveChangesResult current2 in enumerable)
				{
					yield return current2;
				}
			}
			yield break;
		}
		private IEnumerable<SaveChangesResult> GetChangeRequestsFromUnchangedTree(SaveChangesResultCollection results, Entity entity, IEnumerable<Entity> path, IList<LinkDescriptor> circularLinks)
		{
			List<LinkDescriptor> list = new List<LinkDescriptor>();
			var list2 = (
				from relationship in entity.RelatedEntities
				from target in relationship.Value.Entities
				group new
				{
					Relationship = relationship,
					Target = target
				} by target into grp
				select new
				{
					Target = grp.Key,
					Relationships = (
						from g in grp
						select g.Relationship).ToList<KeyValuePair<Relationship, EntityCollection>>()
				}).ToList();
			foreach (var current in list2)
			{
				Entity target2 = current.Target;
				List<LinkDescriptor> list3 = new List<LinkDescriptor>();
				foreach (KeyValuePair<Relationship, EntityCollection> current2 in current.Relationships)
				{
					LinkDescriptor linkDescriptor;
					if (this._bindings.TryGetValue(new LinkDescriptor(entity, current2.Key, target2), out linkDescriptor) && linkDescriptor.State == EntityStates.Added)
					{
						list3.Add(linkDescriptor);
					}
				}
				if (!path.Contains(target2))
				{
					IEnumerable<Entity> path2 = path.Concat(new Entity[]
					{
						target2
					});
					IEnumerable<SaveChangesResult> enumerable = (target2.EntityState == EntityState.Unchanged) ? this.GetChangeRequestsFromUnchangedTree(results, target2, path2, circularLinks) : this.GetChangeRequestsFromChangedTree(results, target2, path2, circularLinks);
					foreach (SaveChangesResult current3 in enumerable)
					{
						yield return current3;
					}
					if (list3.Any<LinkDescriptor>())
					{
						list.AddRange(list3);
					}
				}
				else if (list3.Any<LinkDescriptor>())
				{
					foreach (LinkDescriptor current4 in list3)
					{
						circularLinks.Add(current4);
						OrganizationServiceContext.SetNewId(current4.Source);
						OrganizationServiceContext.SetNewId(current4.Target);
					}
				}
			}
			if (list.Any<LinkDescriptor>())
			{
				IEnumerable<SaveChangesResult> enumerable2 = this.ToAssociateResults(list, results);
				foreach (SaveChangesResult current5 in enumerable2)
				{
					yield return current5;
				}
				foreach (LinkDescriptor link in list)
				{
					this.DetachLinkTrackingAndRemoveEntity(link);
					EntityDescriptor existingEntity;
					if (!this._bindings.Values.Any((LinkDescriptor b) => b.Target == link.Target) && this._descriptors.TryGetValue(link.Target, out existingEntity))
					{
						this.DetachEntityTracking(existingEntity);
					}
				}
			}
			this._roots.Remove(entity);
			yield break;
		}
		private IEnumerable<SaveChangesResult> GetChangeRequestsFromSubtree(SaveChangesResultCollection results, Entity entity, IEnumerable<Entity> localPath, IList<LinkDescriptor> localCircularLinks, IEnumerable<Entity> path, IList<LinkDescriptor> circularLinks)
		{
			var list = (
				from relationship in entity.RelatedEntities
				from target in relationship.Value.Entities
				group new
				{
					Relationship = relationship,
					Target = target
				} by target into grp
				select new
				{
					Target = grp.Key,
					Relationships = (
						from g in grp
						select g.Relationship).ToList<KeyValuePair<Relationship, EntityCollection>>()
				}).ToList();
			foreach (var current in list)
			{
				Entity target2 = current.Target;
				List<LinkDescriptor> list2 = new List<LinkDescriptor>();
				foreach (KeyValuePair<Relationship, EntityCollection> current2 in current.Relationships)
				{
					LinkDescriptor linkDescriptor;
					if (this._bindings.TryGetValue(new LinkDescriptor(entity, current2.Key, target2), out linkDescriptor))
					{
						if (linkDescriptor.State == EntityStates.Added)
						{
							list2.Add(linkDescriptor);
						}
						else
						{
							this.DetachLinkTrackingAndRemoveEntity(linkDescriptor);
						}
					}
				}
				bool flag = localPath.Contains(target2);
				bool flag2 = path.Contains(target2);
				if (!flag && !flag2)
				{
					IEnumerable<Entity> localPath2 = localPath.Concat(new Entity[]
					{
						target2
					});
					IEnumerable<Entity> path2 = path.Concat(new Entity[]
					{
						target2
					});
					IEnumerable<SaveChangesResult> enumerable = list2.Any<LinkDescriptor>() ? this.GetChangeRequestsFromSubtree(results, target2, localPath2, localCircularLinks, path2, circularLinks) : ((target2.EntityState == EntityState.Unchanged) ? this.GetChangeRequestsFromUnchangedTree(results, target2, path2, circularLinks) : this.GetChangeRequestsFromChangedTree(results, target2, path2, circularLinks));
					foreach (SaveChangesResult current3 in enumerable)
					{
						yield return current3;
					}
				}
				else if (list2.Any<LinkDescriptor>())
				{
					foreach (LinkDescriptor current4 in list2)
					{
						if (flag)
						{
							localCircularLinks.Add(current4);
						}
						else
						{
							circularLinks.Add(current4);
						}
						this.DetachLinkTrackingAndRemoveEntity(current4);
						OrganizationServiceContext.SetNewId(current4.Source);
						OrganizationServiceContext.SetNewId(current4.Target);
					}
				}
			}
			yield break;
		}
		private static OrganizationRequest GetRequest(Entity entity)
		{
			if (entity.EntityState == EntityState.Created)
			{
				return new CreateRequest
				{
					Target = entity
				};
			}
			if (entity.EntityState == EntityState.Changed)
			{
				return new UpdateRequest
				{
					Target = entity
				};
			}
			throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The '{0}' entity must be in the created or changed state.", new object[]
			{
				entity.LogicalName
			}));
		}
		private SaveChangesResult GetSaveChangesResult(SaveChangesResultCollection results, Entity entity)
		{
			OrganizationRequest request = OrganizationServiceContext.GetRequest(entity);
			CreateRequest createRequest = request as CreateRequest;
			if (createRequest != null)
			{
				OrganizationServiceContext.TraverseEntityGraph(createRequest.Target, new Action<Entity>(OrganizationServiceContext.SetNewId), delegate(Entity source, Relationship relationship, Entity target)
				{
				}, new Entity[]
				{
					createRequest.Target
				}).ToList<Entity>();
			}
			SaveChangesResult saveChangesResult = this.SaveChange(request, results);
			CreateResponse createResponse = saveChangesResult.Response as CreateResponse;
			if (createResponse != null && entity.Id == Guid.Empty)
			{
				entity.Id = createResponse.id;
			}
			return saveChangesResult;
		}
		private void DetachOnSave(Entity entity)
		{
			IEnumerable<Entity> enumerable = this.DetachEntityGraph(entity);
			foreach (Entity current in enumerable)
			{
				bool flag = true;
				foreach (LinkDescriptor current2 in this.GetTargetingLinks(current).ToList<LinkDescriptor>())
				{
					if (current2.State == EntityStates.Added)
					{
						current2.Target.RelatedEntities.ClearInternal();
						if (!this.IsAttached(current2.Target))
						{
							this.AttachEntityGraph(current2.Target, EntityStates.Unchanged);
							current2.Target.SetEntityStateInternal(new EntityState?(EntityState.Unchanged));
							flag = false;
						}
					}
					else
					{
						this.DetachLinkTrackingAndRemoveEntity(current2);
					}
				}
				if (flag)
				{
					current.SetEntityStateInternal(null);
				}
			}
		}
		private void DetachAllOnSave()
		{
			foreach (EntityDescriptor current in this._descriptors.Values.ToList<EntityDescriptor>())
			{
				this.DetachEntityTracking(current);
			}
			foreach (LinkDescriptor current2 in this._bindings.Values.ToList<LinkDescriptor>())
			{
				this.DetachLinkTracking(current2);
			}
			this._roots.Clear();
		}
		private static EntityReferenceCollection ToEntityReferenceCollection(IEnumerable<LinkDescriptor> links)
		{
			EntityReferenceCollection entityReferenceCollection = new EntityReferenceCollection();
			entityReferenceCollection.AddRange(
				from b in links
				select b.Target.ToEntityReference());
			return entityReferenceCollection;
		}
		private static IEnumerable<AssociateRequest> ToAssociateRequests(IEnumerable<LinkDescriptor> links)
		{
			return 
				from link in links
				group link by new
				{
					link.Source,
					link.Relationship
				} into grp
				select new AssociateRequest
				{
					Target = grp.Key.Source.ToEntityReference(),
					Relationship = grp.Key.Relationship,
					RelatedEntities = OrganizationServiceContext.ToEntityReferenceCollection(grp)
				};
		}
		private IEnumerable<SaveChangesResult> ToAssociateResults(IEnumerable<LinkDescriptor> links, IList<SaveChangesResult> results)
		{
			IEnumerable<AssociateRequest> source = OrganizationServiceContext.ToAssociateRequests(links);
			return 
				from associate in source
				select this.SaveChange(associate, results);
		}
		private static void SetNewId(Entity entity)
		{
			if (entity.EntityState == EntityState.Created && entity.Id == Guid.Empty)
			{
				Guid id = OrganizationServiceContext.CreateSequentialGuid();
				entity.Id = id;
			}
		}
		[SecuritySafeCritical]
		private static Guid CreateSequentialGuid()
		{
			Guid empty = Guid.Empty;
			long num;
			try
			{
				new PermissionSet(PermissionState.Unrestricted).Assert();
				num = NativeMethods.UuidCreateSequential(ref empty);
			}
			finally
			{
				CodeAccessPermission.RevertAssert();
			}
			if (0L != num)
			{
				return Guid.NewGuid();
			}
			byte[] array = empty.ToByteArray();
			byte b = array[2];
			array[2] = array[1];
			array[1] = b;
			b = array[3];
			array[3] = array[0];
			array[0] = b;
			b = array[4];
			array[4] = array[5];
			array[5] = b;
			b = array[6];
			array[6] = array[7];
			array[7] = b;
			Guid result = new Guid(array);
			return result;
		}
		internal Entity MergeEntity(Entity entity)
		{
			if (this.MergeOption == MergeOption.NoTracking || entity == null || entity.Id == Guid.Empty)
			{
				return entity;
			}
			EntityDescriptor entityDescriptor = new EntityDescriptor(EntityStates.Unchanged, entity.ToEntityReference(), entity);
			EntityDescriptor entityDescriptor2;
			this._identityToDescriptor.TryGetValue(entityDescriptor.Identity, out entityDescriptor2);
			if (entityDescriptor2 != null)
			{
				if (this.MergeOption == MergeOption.AppendOnly || (this.MergeOption == MergeOption.PreserveChanges && entityDescriptor2.State != EntityStates.Unchanged))
				{
					return entityDescriptor2.Entity;
				}
				this.DetachEntityTracking(entityDescriptor2);
			}
			this._descriptors[entityDescriptor.Entity] = entityDescriptor;
			this._identityToDescriptor[entityDescriptor.Identity] = entityDescriptor;
			entityDescriptor.Entity.SetEntityStateInternal(new EntityState?(EntityState.Unchanged));
			if (this._trackEntityChanges)
			{
				entityDescriptor.Entity.IsReadOnly = true;
			}
			this.OnBeginEntityTracking(entityDescriptor.Entity);
			return entityDescriptor.Entity;
		}
		private void MergeRelationship(Entity source, Relationship relationship, Entity target, bool isAttached)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			if (isAttached)
			{
				LinkDescriptor linkDescriptor = new LinkDescriptor(source, relationship, target);
				if (this.MergeOption == MergeOption.NoTracking)
				{
					return;
				}
				LinkDescriptor linkDescriptor2;
				this._bindings.TryGetValue(linkDescriptor, out linkDescriptor2);
				if (linkDescriptor2 != null && (this.MergeOption == MergeOption.AppendOnly || (this.MergeOption == MergeOption.PreserveChanges && linkDescriptor2.State != EntityStates.Unchanged)))
				{
					OrganizationServiceContext.AddRelationshipIfNotContains(source, relationship, linkDescriptor2.Target);
					return;
				}
				this._bindings[linkDescriptor] = linkDescriptor;
				this.OnBeginLinkTracking(linkDescriptor.Source, linkDescriptor.Relationship, linkDescriptor.Target);
			}
			if (!source.RelatedEntities.Contains(relationship))
			{
				EntityCollection entityCollection = new EntityCollection(new Entity[]
				{
					target
				});
				entityCollection.EntityName = target.LogicalName;
				source.RelatedEntities.SetItemInternal(relationship, entityCollection);
				return;
			}
			EntityReference targetReference = target.ToEntityReference();
			DataCollection<Entity> entities = source.RelatedEntities[relationship].Entities;
			Entity entity = entities.SingleOrDefault((Entity e) => object.Equals(e.ToEntityReference(), targetReference));
			if (entity != null)
			{
				entities.Remove(entity);
			}
			entities.Add(target);
		}
		private static void AddRelationshipIfNotContains(Entity source, Relationship relationship, Entity target)
		{
			if (!source.RelatedEntities.Contains(relationship))
			{
				EntityCollection entityCollection = new EntityCollection(new Entity[]
				{
					target
				});
				entityCollection.EntityName = target.LogicalName;
				source.RelatedEntities.SetItemInternal(relationship, entityCollection);
				return;
			}
			if (!source.RelatedEntities[relationship].Entities.Contains(target))
			{
				source.RelatedEntities[relationship].Entities.Add(target);
			}
		}
		private static bool RemoveRelationshipIfContains(LinkDescriptor existingLink)
		{
			return OrganizationServiceContext.RemoveRelationshipIfContains(existingLink.Source, existingLink.Relationship, existingLink.Target);
		}
		private static bool RemoveRelationshipIfContains(Entity source, Relationship relationship, Entity target)
		{
			if (source.RelatedEntities.Contains(relationship))
			{
				EntityCollection entityCollection = source.RelatedEntities[relationship];
				bool result = entityCollection.Entities.Remove(target);
				if (entityCollection.Entities.Count == 0)
				{
					source.RelatedEntities.RemoveInternal(relationship);
				}
				return result;
			}
			return false;
		}
		private static void CheckEntitySubclass(Type entityType)
		{
			if (!entityType.IsSubclassOf(typeof(Entity)))
			{
				throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified type '{0}' must be a subclass of '{1}'.", new object[]
				{
					entityType,
					typeof(Entity)
				}));
			}
			if (StringExtensions.IsNullOrWhiteSpace(KnownProxyTypesProvider.GetInstance(true).GetNameForType(entityType)))
			{
				throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified type '{0}' is not a known entity type.", new object[]
				{
					entityType
				}));
			}
		}
		private EntityCollection GetRelatedEntities(Entity entity, Relationship relationship)
		{
			string relatedEntityName = this.GetRelatedEntityName(entity, relationship);
			RelationshipQueryCollection relatedEntitiesQuery = new RelationshipQueryCollection
			{

				{
					relationship,
					new QueryExpression(relatedEntityName)
					{
						ColumnSet = new ColumnSet(true)
					}
				}
			};
			EntityReference target = new EntityReference(entity.LogicalName, entity.Id);
			RetrieveRequest request = new RetrieveRequest
			{
				Target = target,
				ColumnSet = new ColumnSet(),
				RelatedEntitiesQuery = relatedEntitiesQuery
			};
			RetrieveResponse retrieveResponse = this.Execute(request) as RetrieveResponse;
			RelatedEntityCollection relatedEntities = retrieveResponse.Entity.RelatedEntities;
			if (!relatedEntities.Contains(relationship))
			{
				return null;
			}
			return relatedEntities[relationship];
		}
		private string GetRelatedEntityName(Entity entity, Relationship relationship)
		{
			if (relationship.PrimaryEntityRole.HasValue)
			{
				return entity.LogicalName;
			}
			RetrieveRelationshipResponse retrieveRelationshipResponse = this.Execute(new RetrieveRelationshipRequest
			{
				Name = relationship.SchemaName
			}) as RetrieveRelationshipResponse;
			OneToManyRelationshipMetadata oneToManyRelationshipMetadata = retrieveRelationshipResponse.RelationshipMetadata as OneToManyRelationshipMetadata;
			if (oneToManyRelationshipMetadata != null)
			{
				if (!(oneToManyRelationshipMetadata.ReferencingEntity == entity.LogicalName))
				{
					return oneToManyRelationshipMetadata.ReferencingEntity;
				}
				return oneToManyRelationshipMetadata.ReferencedEntity;
			}
			else
			{
				ManyToManyRelationshipMetadata manyToManyRelationshipMetadata = retrieveRelationshipResponse.RelationshipMetadata as ManyToManyRelationshipMetadata;
				if (manyToManyRelationshipMetadata == null)
				{
					throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to load the '{0}' relationship.", new object[]
					{
						relationship.SchemaName
					}));
				}
				if (!(manyToManyRelationshipMetadata.Entity1LogicalName == entity.LogicalName))
				{
					return manyToManyRelationshipMetadata.Entity1LogicalName;
				}
				return manyToManyRelationshipMetadata.Entity2LogicalName;
			}
		}
		private static IEnumerable<Entity> TraverseEntityGraph(Entity entity, Action<Entity> onEntity, Action<Entity, Relationship, Entity> onLink)
		{
			return OrganizationServiceContext.TraverseEntityGraph(entity, onEntity, onLink, new Entity[0]);
		}
		private static IEnumerable<Entity> TraverseEntityGraph(Entity entity, Action<Entity> onEntity, Action<Entity, Relationship, Entity> onLink, IEnumerable<Entity> path)
		{
			onEntity(entity);
			yield return entity;
			foreach (Tuple<Entity, Relationship, Entity> current in OrganizationServiceContext.TraverseRelatedEntities(entity))
			{
				Entity item = current.Item1;
				Relationship item2 = current.Item2;
				Entity item3 = current.Item3;
				onLink(item, item2, item3);
				if (!path.Contains(item3))
				{
					IEnumerable<Entity> path2 = path.Concat(new Entity[]
					{
						item3
					});
					foreach (Entity current2 in OrganizationServiceContext.TraverseEntityGraph(item3, onEntity, onLink, path2))
					{
						yield return current2;
					}
				}
			}
			yield break;
		}
		private static IEnumerable<Tuple<Entity, Relationship, Entity>> TraverseRelatedEntities(Entity entity)
		{
			return 
				from relatedEntity in entity.RelatedEntities.ToList<KeyValuePair<Relationship, EntityCollection>>()
				let relationship = relatedEntity.Key
				let entities = relatedEntity.Value.Entities.ToList<Entity>()
				from target in entities
				select new Tuple<Entity, Relationship, Entity>(entity, relationship, target);
		}
		private IEnumerable<Entity> TraverseEntityGraph(Entity graph, Action<EntityDescriptor> onEntity, Action<LinkDescriptor> onLink)
		{
			return OrganizationServiceContext.TraverseEntityGraph(graph, delegate(Entity entity)
			{
				EntityDescriptor obj;
				if (this._descriptors.TryGetValue(entity, out obj))
				{
					onEntity(obj);
				}
			}, delegate(Entity source, Relationship relationship, Entity target)
			{
				LinkDescriptor obj;
				if (this._bindings.TryGetValue(new LinkDescriptor(source, relationship, target), out obj))
				{
					onLink(obj);
				}
			});
		}
		private IEnumerable<LinkDescriptor> GetTargetingLinks(Entity target)
		{
			return 
				from b in this._bindings.Values
				where b.Target == target
				select b;
		}
		private EntityDescriptor EnsureTracked(Entity entity, string parameterName)
		{
			if (entity == null)
			{
				throw new ArgumentNullException(parameterName);
			}
			EntityDescriptor result;
			if (!this._descriptors.TryGetValue(entity, out result))
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The context is not currently tracking the '{0}' entity.", new object[]
				{
					entity.LogicalName
				}));
			}
			return result;
		}
		private bool EnsureRelatable(Entity source, Relationship relationship, Entity target, EntityStates state)
		{
			if (relationship == null)
			{
				throw new ArgumentNullException("relationship");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			EntityDescriptor entityDescriptor = this.EnsureTracked(source, "source");
			if (object.ReferenceEquals(source, target))
			{
				throw new InvalidOperationException("The entity cannot link to itself.");
			}
			if (source.Id != Guid.Empty && target.Id != Guid.Empty && source.Id == target.Id && string.Equals(source.LogicalName, target.LogicalName, StringComparison.Ordinal))
			{
				throw new InvalidOperationException("The entity cannot link to itself.");
			}
			EntityDescriptor entityDescriptor2 = null;
			if (target != null || (state != EntityStates.Modified && state != EntityStates.Unchanged))
			{
				entityDescriptor2 = this.EnsureTracked(target, "target");
			}
			if ((state == EntityStates.Added || state == EntityStates.Unchanged) && (entityDescriptor.State == EntityStates.Deleted || (entityDescriptor2 != null && entityDescriptor2.State == EntityStates.Deleted)))
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "One or both of the ends of the '{0}' relationship is in the deleted state.", new object[]
				{
					relationship.SchemaName
				}));
			}
			if ((state != EntityStates.Deleted && state != EntityStates.Unchanged) || (entityDescriptor.State != EntityStates.Added && (entityDescriptor2 == null || entityDescriptor2.State != EntityStates.Added)))
			{
				return false;
			}
			if (state != EntityStates.Deleted)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "One or both of the ends of the '{0}' relationship is in the added state.", new object[]
				{
					relationship.SchemaName
				}));
			}
			return true;
		}
		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				this.ClearChanges();
			}
		}
		protected virtual void OnExecuting(OrganizationRequest request)
		{
		}
		protected virtual void OnExecute(OrganizationRequest request, OrganizationResponse response)
		{
		}
		protected virtual void OnExecute(OrganizationRequest request, Exception exception)
		{
		}
		protected virtual void OnSavingChanges(SaveChangesOptions options)
		{
		}
		protected virtual void OnSaveChanges(SaveChangesResultCollection results)
		{
		}
		protected virtual void OnBeginEntityTracking(Entity entity)
		{
		}
		protected virtual void OnEndEntityTracking(Entity entity)
		{
		}
		protected virtual void OnBeginLinkTracking(Entity source, Relationship relationship, Entity target)
		{
		}
		protected virtual void OnEndLinkTracking(Entity entity, Relationship relationship, Entity target)
		{
		}
	}
}
